package main.java.com.chankudo.thread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockWaitQueue {
    private Lock lock;
    private Condition queueEmptyCondition;
    private Condition queueFullCondition;
    private int [] queue;
    private int index;
    private int size;

    public LockWaitQueue(int size) {
        this.queue = new int[size];
        this.size = size;
        this.index = 0;

        // 申请一个对象锁
        // 带参数true的话会采用公平的策略，倾向于等待时间长的线程。但是这样会导致效率非常低下，通常不打开。
        lock = new ReentrantLock(true);
        //lock = new ReentrantLock();

        // new 一个条件，表示队列为空
        queueEmptyCondition = lock.newCondition();

        // new 一个条件，表示队列满了
        queueFullCondition = lock.newCondition();

    }

    public  int get() throws InterruptedException{
        //调用lock方法，在此之后的代码块只能运行单个线程进入
        lock.lock();
        try{
            while(index<=0){//队列为空
                queueEmptyCondition.await();
            }
            // 接下来做的事情就是修改被保护的变量
            // 这个时候消费一个元素
            index-=1;
            int ele = queue[index];
            // 通知所有的消费者可以来消费了
            // signal和signalAll方法的区别：
            // signal随机唤醒一个线程，但如果这个线程仍然不能运行，再次阻塞的化，系统会进入死锁。
            // 但是它比signalAll高效，sigalAll会唤醒所有线程，竞争被保护的对象。
            queueEmptyCondition.signalAll();
            System.out.println("consume a ele:" + ele);
            return ele;
        }finally{
            lock.unlock();
        }

    }

    public void put() throws InterruptedException {
        //调用lock方法，在此之后的代码块只能运行单个线程进入
        lock.lock();
        try{
            while(index>=size){
                queueFullCondition.await(); // 队列满了，生产者等待。
            }
            // 这个时候队列有位置，放一个元素
            int tmpEle = (int)(Math.random()*100);
            System.out.println("produce a ele:" + tmpEle);
            queue[index] = tmpEle;
            index+=1;
            queueFullCondition.signalAll();
            return;
        }finally{
            lock.unlock();
        }
        
    }
}
